// exception.cc 
//      Entry point into the Nachos kernel from user programs.
//      There are two kinds of things that can cause control to
//      transfer back to here from user code:
//
//      syscall -- The user code explicitly requests to call a procedure
//      in the Nachos kernel.  Right now, the only function we support is
//      "Halt".
//
//      exceptions -- The user code does something that the CPU can't handle.
//      For instance, accessing memory that doesn't exist, arithmetic errors,
//      etc.  
//
//      Interrupts (which can also cause control to transfer from user
//      code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include "console.h"
#include "userthread.h"
#include "synch.h"
#include "userproc.h"

//----------------------------------------------------------------------
// UpdatePC : Increments the Program Counter register in order to resume
// the user program immediately after the "syscall" instruction.
//----------------------------------------------------------------------
static void
UpdatePC ()
{
    int pc = machine->ReadRegister (PCReg);
    machine->WriteRegister (PrevPCReg, pc);
    pc = machine->ReadRegister (NextPCReg);
    machine->WriteRegister (PCReg, pc);
    pc += 4;
    machine->WriteRegister (NextPCReg, pc);
}

#ifdef CHANGED

//----------------------------------------------------------------------
// CopyStringFromMachine : Copy a string from the MIPS virtual memory
// to a string used on linux machine. The string is read until the size
// is reached. A \0 is forced at the end of string copy to grant security
//----------------------------------------------------------------------

void CopyStringFromMachine(int from, char *to, unsigned size) {
	
	int i = 0;
	char c;
	int val = 0;
	//char to[MAX_STRING_SIZE];
	do {
		machine->ReadMem(from + i, 1, &val);
		c = (char) val;
		to[i] = c;
		i++;
		
	} while(i < (int) size && c != '\0' && c != EOF);
	to[i] = '\0';
	/*do *(to+i++) = '\0';
	  while(i<size);*/
	
}


//----------------------------------------------------------------------
// CopyStringToMachine : Copy a string from the Linux physical memory
// to a string used on MIPS machine. The string is copied in virtual memory
// until size is reached, or EOF, \0, \n, \r is encountered. To ensure
// security system, a \0 is added to the string.
//----------------------------------------------------------------------
void CopyStringToMachine(int start, char *s, int size) {
	int i = 0;
	while (i < size && s[i] != EOF && s[i] != '\0' && s[i] != '\n' && s[i] != '\r') {
		//if (s[i] == EOF)
		
		machine->WriteMem((int)start + i, 1, (int)s[i]);
		i++;
	}

	// On ajoute \0 a la fin
	machine->WriteMem((int)start + i, 1, (int)s[i]);

}

#endif // CHANGED


//----------------------------------------------------------------------
// ExceptionHandler
//      Entry point into the Nachos kernel.  Called when a user program
//      is executing, and either does a syscall, or generates an addressing
//      or arithmetic exception.
//
//      For system calls, the following is the calling convention:
//
//      system call code -- r2
//              arg1 -- r4
//              arg2 -- r5
//              arg3 -- r6
//              arg4 -- r7
//
//      The result of the system call, if any, must be put back into r2. 
//
// And don't forget to increment the pc before returning. (Or else you'll
// loop making the same system call forever!
//
//      "which" is the kind of exception.  The list of possible exceptions 
//      are in machine.h.
//----------------------------------------------------------------------

void
ExceptionHandler (ExceptionType which)
{
	int type = machine->ReadRegister(2);
#ifndef CHANGED // Noter le if*n*def
    if ((which == SyscallException) && (type == SC_Halt)) {
		DEBUG('a', "Shutdown, initiated by user program.\n");
        interrupt->Halt();
    } else {
        printf("Unexpected user mode exception %d %d\n", which, type);
        ASSERT(FALSE);
    }
#else // CHANGED
    if (which == SyscallException) {
      switch (type) {
		  case SC_Halt: {
			DEBUG('a', "Shutdown, initiated by user program.\n");
			while( currentThread->space->threadBitMap->NumClear() < currentThread->space->GetNbPiles() - 1) {
				currentThread->Yield();
			}
			interrupt->Halt();
			break;
		  }
		  case SC_Exit: {
			DEBUG('a', "Exit, initiated by user program.\n");
			while( currentThread->space->threadBitMap->NumClear() < currentThread->space->GetNbPiles() - 1) {
				currentThread->Yield();
				//printf("NumClear : %d \n", currentThread->space->threadBitMap->NumClear());
			}
			 //printf ("NumForks: %d \n", machine->GetNumForks());
			if (machine->GetNumForks() > 0){
				 	machine->NumForksDown();
				 	((Semaphore*)(machine->sem_proc[currentThread->thread_id]))->V();
					//delete currentThread->space;
				 	currentThread->Finish ();
					printf ("Test2: %d \n", machine->GetNumForks());
				 }
				 else{
			
		    	interrupt->Halt (); }
			/*
			while(1){
					currentThread->Yield ();
				}
			*/
			printf("Valeur retour : %d. \n", machine->ReadRegister(2));
			break;
		  }
		  case SC_PutChar: {
			int param = machine->ReadRegister(4); // Getting the char to write
			//printf("ExceptionHandler : SC_PutChar : %c\n", (char)param);
			synchconsole->SynchPutChar((char) param); // Writting the char to conolse
			break;
		  }
		  case SC_PutString: {
			// 	printf(""); 
				// Tab of char to store the string readed
			  	char chaine[MAX_STRING_SIZE]; 
			  	// Getting the string to write
			  	int param = machine->ReadRegister(4); 
			  	// Getting the string from virtual memory
			  	CopyStringFromMachine((int) param, chaine,MAX_STRING_SIZE); 
			  	// Writting it to the console
			  	synchconsole->SynchPutString(chaine); 
			  	break;
		  }
			case SC_GetChar:	{ 
				// Reading a char on console
				int val_ret = synchconsole->SynchGetChar();
				// Storing the readed char in R2, for return
				machine->WriteRegister(2, (int) val_ret); 
				break;
			}
			case SC_GetString:	{
				printf("");
				char chaine[MAX_STRING_SIZE];
				// Reading the first parameter (addr to store the string)
				int param1 = machine->ReadRegister(4);
				// Reading the second parameter (size of the string)
				int param2 = machine->ReadRegister(5);
				// Getting the string from console
				synchconsole->SynchGetString(chaine, (int) param2);
				// Copying string to virtual memory
				CopyStringToMachine(param1, chaine, param2);
				
				break;
			}
			case SC_GetInt: {
				
				int entier;
				int param1 = machine->ReadRegister(4); // Addr to put the readed int
				
				synchconsole->SynchGetInt(&entier); // Getting the int from the console
				machine->WriteMem(param1, sizeof(int), (int)entier); // Storing the int to virtual memory
				break;
			}
			case SC_PutInt: {
				int param1 = machine->ReadRegister(4); // Addr of the int to write
				
				synchconsole->SynchPutInt((int)param1); // Writting the int to the console
				break;
			}
			case SC_UserThreadCreate:
			{
				DEBUG('a',"UserThreadCreate, initiated by user program.\n");
				machine->WriteRegister(2,do_UserThreadCreate(machine->ReadRegister(4),machine->ReadRegister(5)));
				break;
			}
			case SC_UserThreadExit:
			{
				DEBUG('a',"UserThreadExit, initiated by user program.\n");
				do_UserThreadExit();
				break;
			}
			case SC_UserThreadJoin:
			{
				DEBUG('a',"UserThreadJoin, initiated by user program.\n");
				int param1 = machine->ReadRegister(4);

				do_UserThreadJoin(param1);
				break;
			}
			case SC_ForkExec:
			{
				DEBUG('a',"ForkExec, initiated by user program.\n");
				char chaine[MAX_STRING_SIZE];
				int param = machine->ReadRegister(4);
				
				//printf("ForkExec : à %d \n", param);
				CopyStringFromMachine(param, chaine, MAX_STRING_SIZE);
				//printf(">>>>> ForkExec : %s \n", chaine);
				//machine->WriteRegister(2,0);
				machine->WriteRegister(2,do_ForkExec(chaine));
				
				break;
			}
			case SC_UserForkJoin:
			{
				int pid = machine->ReadRegister(4);
				do_UserForkJoin((int)pid);
				break;
			}
			

		  default: {
			printf("Unexpected user mode exception %d %d\n", which, type);
			ASSERT(FALSE);
		  }
      }
      // LB: Do not forget to increment the pc before returning!
      UpdatePC();
      // End of addition
    }

#endif // CHANGED
}
